<?php

// ThermD statistics generator - by ChriX (chrix@woaf.net)

//		 CHANGELOG - mainly for my own use
/*
	V0.1 31082008 - First version, providing image generation, and/via the following functions:
					null   ts_generateThermometer()																	updates the saved output image
					array  ts_readAllTimeMax(thermd sensor id)														returns all time maximum
					array  ts_readAllTimeMin(thermd sensor id)														returns all time minimum
					string ts_readCurrent(thermd sensor id)															returns the current value
					char   ts_currentTrend(thermd sensor id [,sample period in seconds])							returns the current trend (rising/falling/steady)
					string ts_getAverage(thermd sensor id [,sample period in seconds, offset from time()])			returns the average in specified period
					array  ts_getMaxMin(thermd sensor id [,sample period in seconds, offset from time()])			returns the max and minimum in specified period
					
	V0.2 04102008 - Added simple support for rain sensor, and/via the following functions:
					string ts_totalRainfallPeriod([sample period in seconds, offset from time()])					returns total rainfall in given period
					string ts_returnSunrise()																		returns todays sunrise time at specified location
					string ts_returnSunset()																		returns todays sunset time at specified location
					
	V0.21 26102008 - Bug fixes:
					Some functions not returning output rounded to correct number of decimal places
					Some functions had sensor ID set within rather than globally
					
					Additions:
					Check now in place to validate provided save path for resultant image
					Check now in place to validate provided path for source PNG image
					
	V0.22 29102008 - Bug fixes:
					Today's rainfall not calculating correctly (ts_totalRainfallPeriod flaw)
					
					Additions/changes:
					MySQL connection now included in class.
					Renamed class to ThermdStats (from TempStats)
					int    ts_dryDays()																				returns the number of consecutive whole days since last rainfall
					int	   ts_wetDays()																				returns the number of consecutive whole days since last dry day
					
	V0.3 15112008 - Bug fixes:
					Zenith changed to configuration option
					
					Additions/Changes:
					Configuration now included via tempstats-cfg.php rather than in construct function
					Variables added to configuration to accept log id for: dewpoint, humidity, windspeed, windgust, winddirection, windchill, cloudbase
					Added display of dew point reading to image
					Added display of wind chill reading to image
					Added wind map to image
					Added cloud base to image
					Changed ts_readCurrent to accept dp as second parameter
					string	ts_sinceWetDry()																		returns a string quoting the number of days since last dry day, or since last wet day
					array	ts_getBeaufort(int windspeed)															returns an array of the beaufort scale number and description
					string	ts_rainRate()																			returns the calculated hourly amount of rainfall given the last 2 readings
					string	ts_windDirection(int degrees)															returns the 16 point compass direction of the wind given the degrees

	V0.31 16112008 - Bug fixes:
					Wind direction now does not display if no wind
					Fix Mr_p's find and replace hax (change thermosrc to dir path instead of single image)
					
	V0.32 07122008 - Bug fixes:
					Corrected infinite while loop if no rainfall value in current table
					
	V0.4  28022009 - Various bits:
	        Add barometer support
	        Add cloud sensor support
	        Messed around with object vars to allow functions to be used outside of object context
	        Break up ts_generateThermometer function to allow easier customisation of generated image layout
	        Icons and stats are now added individually via 'widgets'
	        Probably reinstated some bugs that were previously fixed :)
*/

function __autoload($cn)
{
  $cfn = explode('_',strtolower($cn));
  require_once('widgets/'.$cfn[1].'.php');
}

class ThermdStats
{

  private static $classversion = 0.4;

	public static $location,$zenith,$wu_station,$wu_password,$wu_submiturl,$rainfall_log_id,
	        $temperature_log_id,$dewpoint_log_id,$dbtunits,$solarsensor_log_id,
	        $windchill_log_id,$windspeed_log_id,$winddir_log_id,$windgust_log_id,$humidity_log_id,
	        $cloudbase_log_id,$pressure_log_id,$cloudsensor_log_id,$loginterval,$dp,$raindp,
	        $trendrange,$thermosrc,$imgsavepath,$units,$windunits,$winddir_offset,$rainunits,
	        $cloudsensor_clear,$cloudsensor_partly,$cloudsensor_cloudy,$cloudsensor_timeoffset;

	private $dbaccess;
	
	public $image_object;
	
	function __construct()
	{
		require_once('tempstats-cfg.php');
	
		$connection = mysql_connect($dbhost,$dbuser,$dbpassword);
		if(!$connection)
		{
			exit("No database connection");
		}
		
		if(!mysql_select_db($dbname,$connection))
		{
			exit("No database selection");
		}
		$this->dbaccess = $connection;
		
		// check to make sure the output dir exists
		if(!file_exists(ThermdStats::$imgsavepath))
		{
			trigger_error("Specified output path is invalid",E_USER_ERROR);
		}
	}

	function ts_getBeaufort($speed=0)
	{
	
		$ans = array();
		switch($speed)
		{
			case $speed<1:
				$ans[0] = 0;
				$ans[1] = 'Calm';
			break;
			case $speed>=1 && $speed<3:
				$ans[0] = 1;
				$ans[1] = 'Light air';
			break;
			case $speed>=3 && $speed<7:
				$ans[0] = 2;
				$ans[1] = 'Light breeze';
			break;
			case $speed>=7 && $speed<12:
				$ans[0] = 3;
				$ans[1] = 'Gentle breeze';
			break;
			case $speed>=12 && $speed<17:
				$ans[0] = 4;
				$ans[1] = 'Moderate breeze';
			break;
			case $speed>=17 && $speed<24:
				$ans[0] = 5;
				$ans[1] = 'Fresh breeze';
			break;
			case $speed>=24 && $speed<30:
				$ans[0] = 6;
				$ans[1] = 'Strong breeze';
			break;
			case $speed>=30 && $speed<38:
				$ans[0] = 7;
				$ans[1] = 'Near gale';
			break;
			case $speed>=38 && $speed<46:
				$ans[0] = 8;
				$ans[1] = 'Fresh gale';
			break;
			case $speed>=46 && $speed<54:
				$ans[0] = 9;
				$ans[1] = 'Strong gale';
			break;
			case $speed>=54 && $speed<63:
				$ans[0] = 10;
				$ans[1] = 'Storm';
			break;
			case $speed>=63 && $speed<73:
				$ans[0] = 11;
				$ans[1] = 'Violent storm';
			break;
			case $speed>=73:
				$ans[0] = 12;
				$ans[1] = 'Hurricane-force';
			break;
		}
		
		return $ans;
	}
	
	function ts_windDirection($degs)
	{
		$compass_points = array(' N','NNE',' NE','ENE',' E','ESE',' SE','SSE',' S','SSW',' SW','WSW',' W','WNW',' NW','NNW');
		
		$point = floor(abs($degs/22.5));
		
		return $compass_points[$point];
	}
	
	function ts_dryDays()
	{
		// find out how many days it was since the last rain - include today (0000 to time())
		$trperiod = time()-gmmktime(0,0,0,gmdate('m'),gmdate('d'),gmdate('Y'));
		$troffset = 0;
		$rainfound = 0;
		$days=0;
		
		while($rainfound==0)
		{
			$raintest = ThermdStats::ts_totalRainfallPeriod($trperiod,$troffset); // todays rain
			if($raintest>0 || $raintest===FALSE) { $rainfound=1; } else { $days++; }
			$troffset += $trperiod;
			$trperiod = 86400;
		}
		
		return $days;
	}
	
	function ts_wetDays()
	{
		// find out how many days it was since the last dry - include today (0000 to time())
		$trperiod = time()-gmmktime(0,0,0,gmdate('m'),gmdate('d'),gmdate('Y'));
		$troffset = 0;
		$rainnotfound = 0;
		$days = 0;
		while($rainnotfound==0)
		{
			$raintest = ThermdStats::ts_totalRainfallPeriod($trperiod,$troffset); // todays rain
			if($raintest==0 || $raintest===FALSE) { $rainnotfound=1; } else { $days++; }
			$troffset += $trperiod;
			$trperiod = 86400;
		}
		
		return $days;
	}
	
	function ts_sinceWetDry()
	{
		// find out how many days since the last dry or wet day, return string as 'X days since last dry|wet day'
		$wd = ThermdStats::ts_wetDays();
		$dd = ThermdStats::ts_dryDays();
		
		$s = 's';
		if($wd>$dd)
		{
			if($wd==1)
			{
				$s = '';
			}
			return $wd." day".$s." since last dry day";
		} elseif($dd>$wd)
		{
			if($dd==1)
			{
				$s = '';
			}
			return $dd." day".$s." since last rainfall";
		} else {
			return FALSE;
		}
	}

	function ts_rainRate()
	{
		// work out the current rain rate, given by the last 2 readings
		$last = ThermdStats::ts_totalRainfallPeriod(ThermdStats::$loginterval);
		
		return number_format(3600/(ThermdStats::$loginterval) * $last,ThermdStats::$raindp,'.','');
	}

	function ts_totalRainfallPeriod($period=86400,$offset=0,$debug=0,$rainunits=NULL,$raindp=NULL)
	{
		// analyse the log period specified and find the total rainfall
		$current = mysql_fetch_array(mysql_query("SELECT `value`,`logtime` FROM `current` WHERE `log_id` = '".ThermdStats::$rainfall_log_id."'"));
		if(!$current)
		{
			return FALSE;
		}


		$rainrows = mysql_query("SELECT `logtime`,`value` FROM `readings` WHERE `log_id` = '".ThermdStats::$rainfall_log_id."' AND (`logtime` <= '".($current['logtime']-$offset)."' AND `logtime` >= '".($current['logtime']-$offset-$period)."') ORDER BY `logtime` ASC;");

		if($debug==1)
		{
			while($rainrow=mysql_fetch_array($rainrows))
			{
				print_r($rainrow);
			}
			
			return;
		}

		if(!$rainrows)
		{
			return "0.00";
		}
	
		$first = 1;
		$rainfalltotal = 0;
		while($rainrow=mysql_fetch_array($rainrows))
		{
			if($first==1)
			{
				//$rainfalltotal = $rainrow['value'];
				$lastone = $rainrow['value'];
				$first = 0;
				continue;
			}
			if($rainrow['value']>$lastone)
			{
				$rainfalltotal += ($rainrow['value']-$lastone);
			} 
			$lastone = $rainrow['value'];
		}

		// convert to mm
		if($rainunits==NULL)
		{
		  $rainunits = ThermdStats::$rainunits;
	  }
		if($rainunits=="mm")
		{
			$rainfalltotal = $rainfalltotal * 25.4;
		}
		
		if($raindp==NULL)
		{
      $raindp = ThermdStats::$raindp;
		}
		return number_format($rainfalltotal,$raindp,'.','');
	}

	function ts_returnSunrise()
	{
		return date_sunrise(time(),SUNFUNCS_RET_STRING,ThermdStats::$location[0],ThermdStats::$location[1],ThermdStats::$zenith);
	}

	function ts_returnSunset()
	{
		return date_sunset(time(),SUNFUNCS_RET_STRING,ThermdStats::$location[0],ThermdStats::$location[1],ThermdStats::$zenith);
	}
	
	function ts_cloudCoverIsDark()
	{
	  $dsr = date_sunrise(time(),SUNFUNCS_RET_TIMESTAMP,ThermdStats::$location[0],ThermdStats::$location[1],ThermdStats::$zenith);
	  $dss = date_sunset(time(),SUNFUNCS_RET_TIMESTAMP,ThermdStats::$location[0],ThermdStats::$location[1],ThermdStats::$zenith);
	  
    if(time() > $dss+ThermdStats::$cloudsensor_timeoffset || time() < $dsr-ThermdStats::$cloudsensor_timeoffset)
    {
      return TRUE;
    } 
    
    return FALSE;
  }
  
  function ts_wUndergroundSubmit()
  {
    // build a get string to submit some data to Weather Underground.
    $wu_request = ThermdStats::$wu_submiturl."?ID=".ThermdStats::$wu_station.'&PASSWORD='.ThermdStats::$wu_password.'&dateutc=';
    $wu_request .= urlencode(gmdate("Y-m-d+G:i:s")).'&action=updateraw&softwaretype='.urlencode('ThermDStats v'.ThermdStats::$classversion);
     
    // fields to add to the end of this request:
    // humidity x
    // dewptf x
    // tempf  x
    // rainin x
    // dailyrainin  x
    // baromin  x
    // solarradiation x
    // windspeedmph
    // windgustmph
    // winddir
    
    $wu_spec = array('humidity' => ThermdStats::ts_readCurrent(ThermdStats::$humidity_log_id,2),
      'dewptf' => array('type' => 'T', 'units' => 'F', 'id' => ThermdStats::$dewpoint_log_id),
      'tempf' => array('type' => 'T', 'units' => 'F', 'id' => ThermdStats::$temperature_log_id),
      'rainin' => array('type' => 'R', 'value' => ThermdStats::ts_totalRainfallPeriod($period,0,0,"in",2)),
      'dailyrainin' => array('type' => 'R', 'value' => ThermdStats::ts_totalRainfallPeriod(secondsSinceMidnight(),0,0,"in",2)),
      'baromin' => ThermdStats::ts_readCurrent(ThermdStats::$pressure_log_id,2),
      'solarradiation' => ThermdStats::ts_readCurrent(ThermdStats::$solarsensor_log_id,2),
      'windspeedmph' => ThermdStats::ts_readCurrent(ThermdStats::$windspeed_log_id,2),
      'windgustmph' => ThermdStats::ts_readCurrent(ThermdStats::$windgust_log_id,2),
      'winddir' => ThermdStats::ts_readCurrent(ThermdStats::$winddir_log_id,2));
                      
    foreach ($wu_spec as $wuvar => $wuval) {
      if(is_array($wuval))
      {
        // if an array specified, some units to convert or something...
        if($wuval['type']=='T')
        {
          if($wuval['units']!=ThermdStats::$dbtunits)
          {
            $wu_request .= '&'.$wuvar.'='.convertToF(ThermdStats::ts_readCurrent($wuval['id'],2));
          }
        } elseif ($wuval['type']=='R')
        {
          $wu_request .= '&'.$wuvar.'='.$wuval['value'];
        }
      } else {
        if($wuval)
        {
          $wu_request .= '&'.$wuvar.'='.$wuval;
        }
      }
    }
    
    echo($wu_request);
    
  }
  
  function ts_cloudCoverCheck()
  {
    if(ThermdStats::ts_cloudCoverIsDark()===TRUE)
    {
      $tempdiff = ThermdStats::ts_readCurrent(ThermdStats::$temperature_log_id) - ThermdStats::ts_readCurrent(ThermdStats::$cloudsensor_log_id);

      if($tempdiff >= ThermdStats::$cloudsensor_clear) 
      {
        return "Clear";
      }
      elseif($tempdiff >= ThermdStats::$cloudsensor_partly)
      {
        return "Partial";
      } else
      {
        return "Cloudy";
      }
    }
    
    return 'N/A';
  }

	function ts_readAllTimeMax($logid)
	{
		$result = mysql_fetch_array(mysql_query("SELECT `logtime`,`value` FROM `readings` WHERE `log_id` = '".$logid."' ORDER BY `value` DESC LIMIT 1;"));

		return array(gmdate('d/m/Y H:i',$result['logtime']),number_format($result['value'],ThermdStats::$dp,'.',''));
	}

	function ts_readAllTimeMin($logid)
	{
			$result = mysql_fetch_array(mysql_query("SELECT `logtime`,`value` FROM `readings` WHERE `log_id` = '".$logid."' ORDER BY `value` ASC LIMIT 1;"));

			return array(gmdate('d/m/Y H:i',$result['logtime']),number_format($result['value'],ThermdStats::$dp,'.',''));
	}

	function ts_currentTrend($logid,$period=3600)
	{
		$current = mysql_fetch_array(mysql_query("SELECT `value`,`logtime` FROM `current` WHERE `log_id` = '".$logid."'"));

		$periodago = mysql_fetch_array(mysql_query("SELECT `value` FROM `readings` WHERE `log_id` = '".$logid."' AND (`logtime` >= '".($current['logtime']-($period+(ThermdStats::$loginterval/2)))."' AND `logtime` <= '".($current['logtime']-($period-(ThermdStats::$loginterval/2)))."')"));
		
		$tempvariation = $current['value']-$periodago['value'];
		$currentrange = abs($tempvariation);

		if($currentrange>ThermdStats::$trendrange)
		{
			if($tempvariation>0)
			{
				return "U";
			} else {
				return "D";
			}
		} else {
			return "S";
		}

		return FALSE;
	}

	function ts_getAverage($logid,$period=86400,$offset=0)
	{
		$current = mysql_fetch_array(mysql_query("SELECT `logtime` FROM `current` WHERE `log_id` = '".$logid."'"));

		$average = mysql_fetch_array(mysql_query("SELECT avg(`value`) FROM `readings` WHERE `log_id` = '".$logid."' AND (`logtime` >= '".($current['logtime']-$period)."' AND `logtime` <= '".($current['logtime']-$offset)."') GROUP BY `log_id`"));

		return number_format($average['avg(`value`)'],ThermdStats::$dp,'.','');
	}

	function ts_getMaxMin($logid,$period=86400,$offset=0)
	{
		$current = mysql_fetch_array(mysql_query("SELECT `logtime` FROM `current` WHERE `log_id` = '".$logid."'"));

		$max_new = mysql_fetch_array(mysql_query("SELECT `value`,`logtime` FROM `readings` WHERE `log_id` = '".$logid."' AND (`logtime` >= '".($current['logtime']-$period)."' AND `logtime` <= '".($current['logtime']-$offset)."') ORDER BY `value` DESC LIMIT 0,1"));

		$min_new = mysql_fetch_array(mysql_query("SELECT `value`,`logtime` FROM `readings` WHERE `log_id` = '".$logid."' AND (`logtime` >= '".($current['logtime']-$period)."' AND `logtime` <= '".($current['logtime']-$offset)."') ORDER BY `value` ASC LIMIT 0,1"));

		return array(number_format($min_new['value'],ThermdStats::$dp,'.',''),number_format($max_new['value'],ThermdStats::$dp,'.',''),$min_new['logtime'],$max_new['logtime']);
	}

	function ts_readCurrent($logid,$dp=NULL)
	{
		$current = mysql_fetch_array(mysql_query("SELECT `value` FROM `current` WHERE `log_id` = '".$logid."'"));
		
		if($current)
		{
			if($dp===NULL)
			{
				$dp = ThermdStats::$dp;
			}
			return number_format($current['value'],$dp,'.','');
		} else {
			return FALSE;
		}
	}

  function ts_startImage($x,$y)
  {
    $this->img_object = imagecreatetruecolor($x,$y);
    imagesavealpha($this->img_object, true);

    $trans_colour = imagecolorallocatealpha($this->img_object, 0, 0, 0, 127);
    imagefill($this->img_object, 0, 0, $trans_colour);
  }

  function ts_completeImage()
  {
    if(!$this->img_object)
    {
      exit();
    }  

    header('Content-type: image/png');

    imagepng($this->img_object);
    imagedestroy($this->img_object);
    
    exit();
  }
}

interface ThermdSingleWidget
{
  public function drawWidget($io,$x,$y);
}

// misc function dump
// from http://theserverpages.com/php/manual/en/function.imagestring.php
function imagestringcentered ($img,$font,$cy,$text,$color) {
 while (strlen($text) * imagefontwidth($font) > imagesx($img)) {
  if ($font > 1) { $font--; }
  else { break; }
 } 
 imagestring($img,$font,imagesx($img) / 2 - strlen($text) * imagefontwidth($font) / 2,$cy,$text,$color);
}

function secondsSinceMidnight()
{
  return time()-gmmktime(0,0,0,gmdate('m'),gmdate('d'),gmdate('Y'));
}

function convertToF($in)
{
  return ((9/5) * $in + 32); 
}

?>
